#import "MpcPlugin.h"
#import "lib_kai_mpc_iphone.h"

@implementation MpcPlugin


+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterMethodChannel* channel = [FlutterMethodChannel
                                     methodChannelWithName:@"mpc_plugin"
                                     binaryMessenger:[registrar messenger]];
    MpcPlugin* instance = [[MpcPlugin alloc] init];
    [registrar addMethodCallDelegate:instance channel:channel];
}

char * toCString(NSString *string) {
    return (char *)string.UTF8String;
}

NSString *CToString(char *value) {
    return [NSString stringWithCString:value encoding:NSUTF8StringEncoding];
}

BOOL isCStringEmpty(char *value) {
    if (value == NULL || strlen(value) == 0) {
        return YES;
    }
    return NO;
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
    
    @try {
//        NSLog(@"method=%@",call.method);
        NSLog(@"getVersion = %lld", getVersion());
        NSDictionary *arguments = [MpcPlugin convertJsonToObject:call.arguments];
//        NSLog(@"arguments=%@",arguments);
        if (!arguments) {
            result([FlutterError errorWithCode:@"-1" message:@"Parameter error" details:nil]);
            return;
        }
        
        if ([call.method isEqual:@"forgingMPC"]) {
            
            NSString *salt = arguments[@"salt"];
            NSString *path = arguments[@"path"];
            
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                
                char *res = forgingMPCJava(toCString(salt), toCString(path));
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    if (isCStringEmpty(res)) {
                        result([FlutterError errorWithCode:@"-1" message:@"The return result is empty" details:nil]);
                        return;
                    }
                    
                    NSString *resString = CToString(res);
                    NSArray *pieces = [resString componentsSeparatedByString:@";"];
                    if (pieces.count != 3) {
                        result([FlutterError errorWithCode:@"-1" message:@"Wrong number of shards" details:nil]);
                        return;
                    }
                    
                    NSDictionary *returnDict = @{
                        @"mpcPieceA": pieces[0],
                        @"mpcPieceB": pieces[1],
                        @"mpcPieceC": pieces[2],
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }
        
        if ([call.method isEqual:@"useMnemonicForgingMPC"]) {
            
            NSString *mne = arguments[@"mne"];
            NSString *salt = arguments[@"salt"];
            NSString *path = arguments[@"path"];
            
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                
                char *res = useMnemonicForgingMPCJava(toCString(mne), toCString(salt),  toCString(path));
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    if (isCStringEmpty(res)) {
                        result([FlutterError errorWithCode:@"-1" message:@"The return result is empty" details:nil]);
                        return;
                    }
                    
                    NSString *resString = CToString(res);
                    NSArray *pieces = [resString componentsSeparatedByString:@";"];
                    if (pieces.count != 3) {
                        result([FlutterError errorWithCode:@"-1" message:@"Wrong number of shards" details:nil]);
                        return;
                    }
                    
                    NSDictionary *returnDict = @{
                        @"mpcPieceA": pieces[0],
                        @"mpcPieceB": pieces[1],
                        @"mpcPieceC": pieces[2],
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }
        
        if ([call.method isEqual:@"verifyMPCFragment"]) {
            
            NSString *piece = arguments[@"frag"];
            
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                long res = verifyMPCFragment(toCString(piece));
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSDictionary *returnDict = @{
                        @"result": @(res),
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }
        
        if ([call.method isEqual:@"recoverMPCFragment"]) {
            
            NSString *piece1 = arguments[@"frag1"];
            NSString *piece2 = arguments[@"frag2"];
            
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                char *res = recoverMPCFragment(toCString(piece1), toCString(piece2));
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSDictionary *returnDict = @{
                        @"result": @(res),
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }
        
        if ([call.method isEqual:@"mpcFragmentGetPublicKey"]) {
            
            NSString *piece = arguments[@"frag"];
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                
                char *res = mpcFragmentGetPublicKey(toCString(piece));
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSDictionary *returnDict = @{
                        @"result": @(res),
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }
        
        if ([call.method isEqual:@"mpcEscape"]) {
            
            NSString *piece1 = arguments[@"frag1"];
            NSString *piece2 = arguments[@"frag2"];
            NSString *salt = arguments[@"salt"];
//            NSLog(@"piece1=%@",piece1);
//            NSLog(@"piece2=%@",piece2);
//            NSLog(@"salt=%@",salt);
             
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                
                char *res = mpcEscapeJava(toCString(piece1), toCString(piece2), toCString(salt));
              
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    NSString *resString = CToString(res);
           
                    NSArray *resp = [resString componentsSeparatedByString:@";"];
                    
                    if (resp.count != 2) {
                        result([FlutterError errorWithCode:@"-1" message:@"mnemonic or path is null" details:nil]);
                        return;
                    }
//                    
//                    NSLog(@"mnemonic=%@",[MpcPlugin convertHexToString: resp[0]]);
//                    NSLog(@"path=%@",[MpcPlugin convertHexToString: resp[1]]);
//                    
//       
                    
                    NSDictionary *returnDict = @{
                        @"mnemonic": [MpcPlugin convertHexToString: resp[0]],
                        @"path": [MpcPlugin convertHexToString: resp[1]],
                    };
                    
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }
        
        if ([call.method isEqual:@"signTx"]) {
            
            NSString *txSigner = arguments[@"tx"];
            NSString *piece1 = arguments[@"mpcA"];
            NSString *piece2 = arguments[@"mpcB"];
            NSString *salt = arguments[@"salt"];
            
            if (!(txSigner && piece1 && piece2 && salt)) {
                result([FlutterError errorWithCode:@"-1" message:@"Parameter missing" details:nil]);
                return;
            }
            
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                
                char *res = signTx(toCString(txSigner), toCString(piece1), toCString(piece2), toCString(salt));
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    NSString *resString = CToString(res);
                    NSArray *resp = [resString componentsSeparatedByString:@";"];
                    
                    if (isCStringEmpty(res)) {
                        result([FlutterError errorWithCode:@"-1" message:@"The return result is empty" details:nil]);
                        return;
                    }
                    
                    NSDictionary *returnDict = @{
                        @"result": resString,
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }

        if ([call.method isEqual:@"solSignTx"]) {

            NSString *txSigner = arguments[@"tx"];
            NSString *piece1 = arguments[@"mpcA"];
            NSString *piece2 = arguments[@"mpcB"];
            NSString *salt = arguments[@"salt"];

            if (!(txSigner && piece1 && piece2 && salt)) {
                result([FlutterError errorWithCode:@"-1" message:@"Parameter missing" details:nil]);
                return;
            }

            dispatch_async(dispatch_get_global_queue(0, 0), ^{

                char *res = solSignTx(toCString(txSigner), toCString(piece1), toCString(piece2), toCString(salt));

                dispatch_async(dispatch_get_main_queue(), ^{

                    NSString *resString = CToString(res);
                    NSArray *resp = [resString componentsSeparatedByString:@";"];

                    if (isCStringEmpty(res)) {
                        result([FlutterError errorWithCode:@"-1" message:@"The return result is empty" details:nil]);
                        return;
                    }

                    NSDictionary *returnDict = @{
                        @"result": resString,
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }

        if ([call.method isEqual:@"upgradeMPC"]) {

            NSString *frag1 = arguments[@"frag1"];
            NSString *frag2 = arguments[@"frag2"];
            NSString *salt = arguments[@"salt"];

            dispatch_async(dispatch_get_global_queue(0, 0), ^{

                char *res = upgradeMPC(toCString(frag1),toCString(frag2), toCString(salt));

                dispatch_async(dispatch_get_main_queue(), ^{

                    if (isCStringEmpty(res)) {
                        result([FlutterError errorWithCode:@"-1" message:@"The return result is empty" details:nil]);
                        return;
                    }

                    NSString *resString = CToString(res);
                    NSArray *pieces = [resString componentsSeparatedByString:@";"];
                    if (pieces.count != 3) {
                        result([FlutterError errorWithCode:@"-1" message:@"Wrong number of shards" details:nil]);
                        return;
                    }

                    NSDictionary *returnDict = @{
                            @"mpcPieceA": pieces[0],
                            @"mpcPieceB": pieces[1],
                            @"mpcPieceC": pieces[2],
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }

        if ([call.method isEqual:@"getAddress"]) {

            NSString *frag1 = arguments[@"frag"];
            NSString *chainId = arguments[@"chainId"];

            dispatch_async(dispatch_get_global_queue(0, 0), ^{

                char *res = getAddress(toCString(frag1),toCString(chainId));

                dispatch_async(dispatch_get_main_queue(), ^{

                    if (isCStringEmpty(res)) {
                        result([FlutterError errorWithCode:@"-1" message:@"The return result is empty" details:nil]);
                        return;
                    }

                    NSString *resString = CToString(res);
//                    NSArray *pieces = [resString componentsSeparatedByString:@" "];
//                    if (pieces.count != 2) {
//                        result([FlutterError errorWithCode:@"-1" message:@"Wrong number of shards" details:nil]);
//                        return;
//                    }

                    NSDictionary *returnDict = @{
//                            @"symbol": resString,
                            @"address": resString,
                            @"chainId": chainId,
                    };
                    result([MpcPlugin convertObjectToJson:returnDict]);
                });
            });
            return;
        }

        if ([call.method isEqual:@"getVersionFromFrag"]) {

            NSString *frag1 = arguments[@"frag"];

            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                int res = getVersionFromFrag(toCString(frag1));
                dispatch_async(dispatch_get_main_queue(), ^{
                    result([NSString stringWithFormat:@"%d", res]);
                });
            });
            return;
        }

        result(FlutterMethodNotImplemented);
        
    } @catch (NSException *exception) {
        result([FlutterError errorWithCode:@"-1" message:@"Removal occurs" details:nil]);
    } @finally {
        
        
    }
}

+ (nullable NSString *)convertObjectToJson:(id)object {
    if ([object isKindOfClass:NSString.class]) {
        return object;
    }
    
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:&error];
    if (!jsonData) {
        return nil;
    }
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    return jsonString;
}


+ (id)convertJsonToObject:(NSString *)jsonString {
    if (![jsonString isKindOfClass:NSString.class]) {
        return jsonString;
    }
    
    NSError *error;
    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    if (!jsonData) {
        return nil;
    }
    
    id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
    return jsonObject;
}

+ (id)convertHexToString:(NSString *)hexString {
  
    NSMutableData *data = [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [hexString length]/2; i++) {
        byte_chars[0] = [hexString characterAtIndex:i*2];
        byte_chars[1] = [hexString characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [data appendBytes:&whole_byte length:1];
    }
    NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    return text;
}

@end
